home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 06 - 1990 / 06.12 Dec 90 / CleanPict Source / CleanMain.c next >
Encoding:
C/C++ Source or Header  |  1990-09-21  |  21.1 KB  |  940 lines  |  [TEXT/KAHL]

  1. /*                                            CleanMain.c                                            */
  2. /*
  3.  * Copyright © 1989, 1990 Martin Minow and MacTutor.
  4.  *
  5.  * You may use this software in any application and
  6.  * redistribute this source without restriction as long
  7.  * as this copyright and permission notice remains intact
  8.  * and the source is not redistributed for profit and you
  9.  * assume all responsibility for the proper operation of
  10.  * this software.
  11.  *
  12.  * Written in Think C.  Set Tabs every 2 characters.
  13.  *
  14.  * Operation:
  15.  *    CleanPICT can read a digitized image stored in a PICT
  16.  *    file (black and white at 300 dpi only) into a work
  17.  *    area.  It then examines the picture for noise (islands
  18.  *    of pixels completely surrounded by the other color)
  19.  *    that are smaller than a selectable threshold. It can
  20.  *    then write the cleaned image to a file.  Note that it
  21.  *    can eliminate islands of white pixels enclosed by the
  22.  *    black image, as well as islands of black pixels on
  23.  *    the white background.
  24.  *
  25.  * Limitations:
  26.  *    CleanPICT was developed to clean out one specific
  27.  *    image (a 300 dpi scanned image).  It is extremely slow
  28.  *    (that PICT requires over an hour on a Mac SE).
  29.  *    CleanPICT keeps the entire PICT in memory, and
  30.  *    consequently needs a 1,200 K partition.  It requires
  31.  *    a 4 Mb computer when run under Multifinder with the
  32.  *    Think C debugger.
  33.  *
  34.  * Homework assignment:
  35.  *    Generalize to arbitrary PICTs (72 dpi resolution, etc.)
  36.  *    Generalize to color.
  37.  *    Speed up.
  38.  *    Use a work file to minimize memory requirement.
  39.  */
  40.  
  41. #include "CleanPICT.h"
  42.  
  43. /*
  44.  * Menu organization
  45.  */
  46. enum Menus {
  47.     MENU_Apple            = 1,
  48.     MENU_File                = 256,
  49.     MENU_Edit                = 257
  50. };
  51.  
  52. enum Apple_Menu {
  53.     Apple_About = 1
  54. };
  55.  
  56. enum File_Menu {
  57.     File_Open        = 1,
  58.     File_Clean,
  59.     File_Set,
  60.     File_Close,
  61.     File_SaveAs,
  62.     File_Debug,
  63.     Unused,
  64.   File_Quit
  65. };
  66.  
  67. enum Edit_Menu {
  68.     Edit_Undo,
  69.     Edit_Unused,
  70.     Edit_Cut,
  71.     Edit_Copy,
  72.     Edit_Paste,
  73.     Edit_Clear
  74. };
  75.  
  76. /*
  77.  * Various stuff in the resource file.
  78.  */
  79. enum {
  80.     WIND_About = 1000,
  81.     DLOG_Set_Threshold = 1000,
  82.     ALRT_Advise = 2000,
  83.     CURS_Spin = 2000,
  84.     STRS_Info = 1
  85. };
  86.  
  87. /*
  88.  * Dialog and Alert item lists.
  89.  */
  90. enum {                            /* DLOG_Set_Threshold                                */
  91.     Thresh_OK = 1,
  92.     Thresh_Cancel,
  93.     Thresh_Text,
  94.     Thresh_Value
  95. };
  96.  
  97. enum {                            /* ALRT_Advise                                            */
  98.     Advise_Save = 1,
  99.     Advise_Discard,
  100.     Advise_Cancel
  101. };
  102.  
  103. /*
  104.  * isOurWindow is TRUE if its argument is our document.
  105.  * isAboutWindow is TRUE if its argument is the progress
  106.  * window.
  107.  */
  108. #define isOurWindow(window)    (                                                        \
  109.             (window) != NIL                                                                        \
  110.         && ((WindowPeek) (window))->windowKind == userKind    \
  111.         && (window) != aboutWindow                                                    \
  112.      )
  113. #define isAboutWindow(window) (                                                    \
  114.             (window) != NIL && (window) == aboutWindow                \
  115.         )
  116.  
  117. MenuHandle            appleMenu;
  118. MenuHandle            fileMenu;
  119. MenuHandle            editMenu;
  120. WindowPtr                aboutWindow;
  121. CursHandle            spinCursor[2];
  122.  
  123. void                        main(void);
  124. Boolean                    do_mouse(EventRecord);
  125. void                        do_command(long);
  126. void                        adjust_menus(WindowPtr);
  127. void                        adjust_edit_menu(Boolean);
  128. Boolean                    handle_events(void);
  129. void                        do_command(long);
  130. void                        setup(void);
  131. void                        do_about(void);
  132. void                        open_document(void);
  133. WindowPtr                new_document(Str255);
  134. Boolean                    close_document(WindowPtr);
  135. void                        save_document(WindowPtr);
  136. void                        set_threshold(WindowPtr);
  137. void                        spin_cursor(void);
  138. void                        show_progress(WindowPtr);
  139. void                        display_statistics(WindowPtr, Boolean);
  140.  
  141. /*
  142.  * main()
  143.  * Initialize the program and run the event loop.
  144.  */
  145. void
  146. main()
  147. {
  148.         EventRecord            event;
  149.         WindowPtr                window;
  150.         GrafPtr                    save_port;
  151.         Rect                        box;
  152.         long                        choice;
  153.         Boolean                    quitNow;        /* <CMD>. seen                */
  154.  
  155.         setup();
  156.         quitNow = FALSE;
  157.         for (;;) {
  158.             SystemTask();
  159.             while (GetNextEvent(everyEvent, &event)
  160.                  && event.what != nullEvent) {
  161.                 if (event.what == activateEvt
  162.                  || event.what == updateEvt)
  163.                      window = (WindowPtr) event.message;
  164.                 else {
  165.                     window = FrontWindow();
  166.                 }
  167.                 switch (event.what) {
  168.                 case mouseDown:
  169.                     do_mouse(event);
  170.                     break;
  171.                 case activateEvt:
  172.                     if (isOurWindow(window) && aboutWindow != NIL)
  173.                         SetWRefCon(aboutWindow, window);
  174.                     break;
  175.                 case updateEvt:
  176.                     GetPort(&save_port);
  177.                     SetPort(window);
  178.                     BeginUpdate(window);
  179.                     if (!EmptyRgn((*window).visRgn)) {
  180.                         EraseRect(&window->portRect);
  181.                         if (isOurWindow(window)) {
  182.                             /*
  183.                              * Note that this must track the screen
  184.                              * display procedure in invert_seen_map().
  185.                              */
  186.                             CopyOSGrafPort(
  187.                                 DOC.pictPort, window, window->visRgn);
  188.                         }
  189.                         else if (isAboutWindow(window))
  190.                             display_statistics(window, TRUE);
  191.                     }
  192.                     EndUpdate(window);
  193.                     SetPort(save_port);
  194.                     break;
  195.                 case keyDown:
  196.                     if ((event.message & charCodeMask) == '.'
  197.                      && (event.modifiers & cmdKey) != 0) {
  198.                          FlushEvents(keyDown | autoKey, 0);
  199.                          quitNow = TRUE;
  200.                         ARROW_CURSOR;
  201.                     }
  202.                     else if ((event.modifiers & cmdKey) != 0) {
  203.                         if (event.what == keyDown) {
  204.                             choice = MenuKey(
  205.                                                     event.message & charCodeMask);
  206.                             if (HiWord(choice) != 0)
  207.                                 do_command(choice);
  208.                             else {
  209.                                 SysBeep(10);        /* Bogus <cmd>    */
  210.                             }
  211.                         }
  212.                     }
  213.                     break;
  214.                 default:
  215.                     break;
  216.                 }
  217.             }
  218.             /*
  219.              * We do the actual cleaning when the
  220.              * machine is otherwise idle.
  221.              */
  222.             window = FrontWindow();
  223.             if (isAboutWindow(window))
  224.                 window = (WindowPtr) GetWRefCon(window);
  225.             if (isOurWindow(window)) {
  226.                 clean_picture(window, quitNow);
  227.                 if (DOC.state == Working) {
  228.                     if (aboutWindow != NIL)
  229.                         display_statistics(aboutWindow, FALSE);
  230.                     spin_cursor();
  231.                     show_progress(window);
  232.                 }
  233.                 else {
  234.                     quitNow = FALSE;
  235.                 }
  236.             }
  237.         }
  238. }
  239.  
  240. /*
  241.  * do_mouse(event)
  242.  * Process a mouse button press, calling handlers as
  243.  * needed.
  244.  */
  245. static Boolean
  246. do_mouse(event)
  247. EventRecord        event;
  248. {
  249.         WindowPtr            window;
  250.         register int    which_part;
  251.         Rect                    box;
  252.         
  253.         which_part = FindWindow(event.where, &window);
  254.         if (which_part == inMenuBar
  255.          && isOurWindow(window) == FALSE)
  256.             window = FrontWindow();
  257.         adjust_menus(window);
  258.         switch (which_part) {
  259.         case inDesk:
  260.             SysBeep(2);
  261.             break;
  262.         case inMenuBar:
  263.             ARROW_CURSOR;
  264.             do_command(MenuSelect(event.where));
  265.             break;
  266.         case inDrag:
  267.             box = screenBits.bounds;
  268.             box.top += GetMBarHeight();
  269.             InsetRect(&box, 4, 4);
  270.             DragWindow(window, event.where, &box);
  271.             break;
  272.         case inContent:
  273.             if (FrontWindow() != window)
  274.                 SelectWindow(window);
  275.             else {
  276.                 SetPort(window);
  277.             }
  278.             break;
  279.         case inGoAway:
  280.             if (isOurWindow(window)
  281.              && TrackGoAway(window, event.where)) {
  282.                 close_document(window);
  283.                 if (aboutWindow != NIL)
  284.                     SetWRefCon(aboutWindow, NIL);
  285.             }
  286.             else if (isAboutWindow(window)
  287.              && TrackGoAway(window, event.where)) {
  288.                  DisposeWindow(window);
  289.                  aboutWindow = NIL;
  290.             }
  291.             break;
  292.         }
  293.         return (FALSE);
  294. }
  295.  
  296. /*
  297.  * do_command()
  298.  * Process a menu command.
  299.  */
  300. void
  301. do_command(choice)
  302. long                choice;
  303. {
  304.         WindowPtr            window;
  305.         int                        item;
  306.         long                    new;
  307.         GrafPtr                save_port;
  308.         Str255                name;
  309.  
  310.         window = FrontWindow();
  311.         item = LoWord(choice);
  312.         switch (HiWord(choice)) {
  313.         case MENU_Apple:
  314.             GetItem(appleMenu, item, &name);
  315.             if (item == Apple_About)
  316.                 make_about_window(window);
  317.             else {
  318.                 adjust_edit_menu(TRUE);
  319.                 GetPort(&save_port);
  320.                 OpenDeskAcc(name);
  321.                 SetPort(save_port);
  322.                 adjust_edit_menu(FALSE);
  323.             }
  324.             break;
  325.         case MENU_File:
  326.             if (isAboutWindow(window))
  327.                 window = (WindowPtr) GetWRefCon(window);
  328.             switch (item) {
  329.             case File_Open:        open_document();                break;
  330.             case File_Clean:
  331.                 if (isOurWindow(window) && DOC.state == Idle)
  332.                     DOC.state = Init;
  333.                 break;
  334.             case File_Close:    close_document(window);    break;
  335.             case File_Set:        set_threshold(window);    break;
  336.             case File_SaveAs:
  337.                 if (isOurWindow(window) == FALSE
  338.                  || DOC.state != Idle)
  339.                     SysBeep(10);
  340.                 else {
  341.                     save_document(window);
  342.                 }
  343.                 break;
  344.             case File_Debug:    Debugger();                            break;
  345.             case File_Quit:
  346.                 /*
  347.                  * A motion to adjourn is always in order.
  348.                  */
  349.                 while (isOurWindow(window)) {
  350.                     if (close_document(window) == FALSE)
  351.                         goto no_exit;
  352.                     window = FrontWindow();
  353.                 }
  354.                 ExitToShell();
  355. no_exit:    break;
  356.             }
  357.             default:
  358.                 break;
  359.         }
  360.         HiliteMenu(0);
  361. }
  362.  
  363. void
  364. make_about_window(window)
  365. WindowPtr        window;
  366. {
  367.         if (aboutWindow == NIL) {
  368.             aboutWindow =
  369.                 GetNewWindow(WIND_About, NIL, -1L);
  370.             if (aboutWindow != NIL)
  371.                 SetWRefCon(aboutWindow, window);
  372.         }
  373.         if (aboutWindow != NIL)
  374.             SelectWindow(aboutWindow);
  375. }
  376.  
  377. /*
  378.  * adjust_menus()
  379.  * Enable and disable menu items as needed.
  380.  */
  381. static void
  382. adjust_menus(window)
  383. WindowPtr            window;
  384. {
  385.         if (isAboutWindow(window))
  386.             window = (WindowPtr) GetWRefCon(window);
  387.         if (isOurWindow(window)) {
  388.             EnableItem(fileMenu, File_Clean);
  389.             EnableItem(fileMenu, File_Set);
  390.             EnableItem(fileMenu, File_Close);
  391.             if (DOC.state == Idle)
  392.                 EnableItem(fileMenu, File_SaveAs);
  393.             else {
  394.                 DisableItem(fileMenu, File_SaveAs);
  395.             }
  396.             CheckItem(
  397.                 fileMenu,
  398.                 File_Clean,
  399.                 DOC.state == Working
  400.             );
  401.         }
  402.         else {
  403.             DisableItem(fileMenu, File_Clean);
  404.             DisableItem(fileMenu, File_Set);
  405.             DisableItem(fileMenu, File_Close);
  406.             DisableItem(fileMenu, File_SaveAs);
  407.         }
  408.         adjust_edit_menu(FALSE);
  409. }
  410.  
  411. /*
  412.  * adjust_edit_menu()
  413.  * Fiddle the Edit menu.  Mostly, enable
  414.  * everything when switching to a desk accessory.
  415.  * Note that CleanPict doesn't have anything
  416.  * "editable" -- it would be reasonable to allow
  417.  * moving the progress info to the Clipboard, though.
  418.  */
  419. void
  420. adjust_edit_menu(enable)
  421. Boolean        enable;
  422. {
  423.         if (enable) {
  424.             EnableItem(editMenu, Edit_Undo);
  425.             EnableItem(editMenu, Edit_Cut);
  426.             EnableItem(editMenu, Edit_Copy);
  427.             EnableItem(editMenu, Edit_Paste);
  428.             EnableItem(editMenu, Edit_Clear);
  429.         }
  430.         else {
  431.             DisableItem(editMenu, Edit_Undo);
  432.             DisableItem(editMenu, Edit_Cut);
  433.             DisableItem(editMenu, Edit_Copy);
  434.             DisableItem(editMenu, Edit_Paste);
  435.             DisableItem(editMenu, Edit_Clear);
  436.         }
  437. }
  438.  
  439. /*
  440.  * open_document()
  441.  * Ask for a file (only allow PICT files).  Read the
  442.  * picture into a new window/document.
  443.  */ 
  444. void
  445. open_document()
  446. {
  447.         WindowPtr                window;
  448.         SFReply                    reply;
  449.         int                            file;
  450.         OSErr                        status;
  451.         static Point        where = { 85, 85 };
  452.         SFTypeList            typeList = { 'PICT' };
  453.  
  454.         SFGetFile(
  455.             where,                                    /* Where on the screen        */
  456.             NIL,                                        /* Unused                                    */
  457.             NIL,                                        /* no file filter                    */
  458.             1,                                            /* Allow one file type        */
  459.             typeList,                                /* according to the list    */
  460.             NIL,                                        /* no dialog hook                    */
  461.             &reply                                    /* reply goes here                */
  462.         );
  463.         if (reply.good) {
  464.             WATCH_CURSOR;
  465.             SetVol(NIL, reply.vRefNum);
  466.              status = FSOpen(reply.fName, reply.vRefNum, &file);
  467.              if (status != noErr)
  468.                  SysBeep(10);
  469.              else {
  470.                 window = new_document(reply.fName);
  471.                 if (window == NIL)
  472.                     SysBeep(10);                /* No memory                            */
  473.                 else {
  474.                     status = read_picture(window, file);
  475.                     if (status != noErr) {
  476.                         DebugStr("\pread_picture failed");
  477.                     }
  478.                 }
  479.                 FSClose(file);
  480.             }
  481.             ARROW_CURSOR;
  482.         }
  483. }
  484.  
  485. /*
  486.  * close_document()
  487.  * Close the document in the current (front) window.
  488.  * Dump picture. Return TRUE on success, FALSE if
  489.  * the user cancels.
  490.  */
  491. Boolean
  492. close_document(window)
  493. WindowPtr            window;
  494. {
  495.         short                size;
  496.         Cell                cell;
  497.  
  498.         if (isOurWindow(window) == FALSE)
  499.             return (TRUE);
  500.         if (DOC.dirty) {
  501.             ParamText(DOC.fileName, NIL, NIL, NIL);
  502.             switch (CautionAlert(ALRT_Advise, NIL)) {
  503.             case Advise_Save:
  504.                 save_document(window);
  505.                 break;
  506.             case Advise_Discard:
  507.                 break;
  508.             case Advise_Cancel:
  509.                 return (FALSE);
  510.             }
  511.         }
  512.         WATCH_CURSOR;
  513.         DeleteOSGrafPort(DOC.pictPort);
  514.         CloseWindow(window);
  515.         DisposPtr(window);
  516.         window = NIL;
  517.         CheckItem(fileMenu, File_Clean, FALSE);
  518.         ARROW_CURSOR;
  519.         return (TRUE);
  520. }
  521.  
  522. /*
  523.  * new_document()
  524.  * Build a document: get memory for the WindowRecord and
  525.  * our attached information.  Offset the window with
  526.  * respect to other windows and make the window.  If
  527.  * this succeeds, read the picture.
  528.  */
  529. WindowPtr
  530. new_document(title)
  531. Str255        title;
  532. {
  533.         WindowPtr            window;
  534.         DocumentPtr        doc;
  535.         Rect                    box;
  536.         static long        sequence;        /* Identify windows                */
  537.         
  538.         doc = (DocumentPtr) NewPtrClear(sizeof (DocumentRecord));
  539.         if (doc == NIL)
  540.             return (NIL);
  541.         /*
  542.          * Start with a tiny window: the picture reader
  543.          * sets the correct size.
  544.          */
  545.         SetRect(&box, 0, GetMBarHeight() * 2, 32, 0);
  546.         box.bottom = box.top + 32;
  547.         window = NewWindow(
  548.                     doc,                                /* Allocated storage            */
  549.                     &box,                                /* Display Rect                        */
  550.                     title,                            /* Title                                    */
  551.                     FALSE,                            /* Invisible on creation    */
  552.                     noGrowDocProc,            /* Window type                        */
  553.                     -1L,                                /* Show in front                    */
  554.                     TRUE,                                /* GoAway box                            */
  555.                     ++sequence                    /* RefCon    (debug only)        */
  556.                 );
  557.         if (window == NIL) {
  558.             DisposPtr(doc);
  559.             return (NIL);
  560.         }
  561.         pstrcpy(DOC.fileName, title);
  562.         DOC.threshold = THRESHOLD;
  563.         SetPort(window);
  564.         return (window);
  565. }
  566.  
  567. /*
  568.  * save_document()
  569.  * Write the current picture as a PICT.  The creator
  570.  * is hard-wired for Canvas.
  571.  */
  572. void
  573. save_document(window)
  574. WindowPtr            window;
  575. {
  576.         SFReply                    reply;
  577.         int                            file;
  578.         OSErr                        status;
  579.         static Point        where = { 85, 85 };
  580.         SFTypeList            typeList = { 'PICT' };
  581.         Str255                    default_filename;
  582.             
  583.         if (isOurWindow(window) == FALSE)
  584.             return;
  585.         pstrcpy(default_filename, "\pNew ");
  586.         pstrcat(default_filename, DOC.fileName);
  587.         SFPutFile(
  588.             where,
  589.             "\pSave PICT as",
  590.             default_filename,
  591.             NIL,
  592.             &reply
  593.         );
  594.         if (reply.good == FALSE)
  595.             goto exit;
  596.         else {
  597.             WATCH_CURSOR;
  598.             (void) FSDelete(reply.fName, reply.vRefNum);
  599.             status = Create(
  600.                                     reply.fName,        /* File name                    */
  601.                                     reply.vRefNum,    /* Volume reference        */
  602.                                     'DAD2',                    /* Creator == Canvas    */
  603.                                     'PICT'                    /* File type                    */
  604.                                 );
  605.             if (status != noErr) {
  606.                 DebugStr("\pCan't create file");
  607.                 goto exit;
  608.             }
  609.             status = FSOpen(reply.fName, reply.vRefNum, &file);
  610.             if (status != noErr) {
  611.                 DebugStr("\pCan't open newly created file");
  612.                 goto exit;
  613.             }
  614.             write_picture(window, file);
  615.             FSClose(file);                                /* Should check for    */
  616.             FlushVol(NIL, reply.vRefNum);    /* Errors here.            */
  617.             DOC.dirty = FALSE;
  618. exit:
  619.             ARROW_CURSOR;
  620.         }
  621. }
  622.  
  623. /*
  624.  * setup()
  625.  * One-time initialization.
  626.  */
  627. void
  628. setup()
  629. {
  630.         InitGraf(&thePort);
  631.         InitFonts();
  632.         FlushEvents(everyEvent, 0);
  633.         InitWindows();
  634.         InitMenus();
  635.         TEInit();
  636.         InitDialogs(NIL);
  637.         InitCursor();
  638.         WATCH_CURSOR;
  639.         MaxApplZone();
  640.         appleMenu = GetMenu(MENU_Apple);
  641.         fileMenu = GetMenu(MENU_File);
  642.         editMenu = GetMenu(MENU_Edit);
  643.         spinCursor[0] =
  644.             (CursHandle) GetResource('CURS', CURS_Spin);
  645.         spinCursor[1] =
  646.             (CursHandle) GetResource('CURS', CURS_Spin + 1);
  647.         AddResMenu(appleMenu, 'DRVR');
  648.         InsertMenu(appleMenu, 0);
  649.         InsertMenu(fileMenu, 0);
  650.         InsertMenu(editMenu, 0);
  651.         DrawMenuBar();
  652.         ARROW_CURSOR;
  653. }
  654.  
  655. /*
  656.  * pstrcat()
  657.  * Concatenate a pascal string to another.
  658.  */
  659. void
  660. pstrcat(out, in)
  661. void                *out;
  662. void                *in;
  663. {
  664.         long            start;
  665.         long            length;        
  666.         
  667.         start = ((unsigned char *) out)[0];
  668.         length = ((unsigned char *) in)[0];
  669.         if ((start + length) > 255)
  670.             length = 255 - start;
  671.         ((unsigned char *) out)[0] = start + length;
  672.         BlockMove(
  673.             ((unsigned char *) in) + 1,
  674.             ((unsigned char *) out) + start + 1,
  675.             length
  676.         );
  677. }
  678.  
  679. /*
  680.  * set_threshold()
  681.  * Ask the user for a threshold value for the current
  682.  * window.
  683.  */
  684. void
  685. set_threshold(window)
  686. WindowPtr            window;
  687. {
  688.         Str255            work;
  689.         long                newSize;
  690.         GrafPtr            old_port;
  691.         DialogPtr        dialog;
  692.         int                    type;
  693.         Handle            handle;
  694.         Rect                box;
  695.         int                    item;
  696.         
  697.         if (isOurWindow(window) == FALSE)
  698.             return;
  699.         GetPort(&old_port);
  700.         dialog = GetNewDialog(DLOG_Set_Threshold, NIL, -1L);
  701.         ShowWindow(dialog);
  702.         SetPort(dialog);
  703. again:
  704.         newSize = DOC.threshold;
  705.         NumToString(newSize, work);
  706.         GetDItem(dialog, Thresh_Value, &type, &handle, &box);
  707.         SetIText(handle, work);
  708.         SelIText(dialog, Thresh_Value, 0, 32767);
  709.         ModalDialog(NIL, &item);
  710.         switch (item) {
  711.         case Cancel:
  712.             break;
  713.         case OK:
  714.             GetDItem(dialog, Thresh_Value, &type, &handle, &box);
  715.             GetIText(handle, work);
  716.             StringToNum(work, &newSize);
  717.             if (newSize <= 0 || newSize >= (XMAX * YMAX)) {
  718.                 SysBeep(10);
  719.                 goto again;
  720.             }
  721.             DOC.threshold = newSize;
  722.             break;
  723.         default:                                /* Can't happen                            */
  724.             break;
  725.         }
  726.         DisposDialog(dialog);
  727.         SetPort(old_port);
  728. }
  729.  
  730. /*
  731.  * spin_cursor()
  732.  * Blink the cursor: called during idle time.
  733.  */
  734. void
  735. spin_cursor()
  736. {
  737.         static int        index = 0;
  738.         static Ulong    last = 0;
  739.         long                    now;
  740.  
  741.         GetDateTime(&now);
  742.         if ((now - last) > 0) {
  743.             index = 1 - index;
  744.             SetCursor(*spinCursor[index]);
  745.             last = now;
  746.         }
  747. }
  748.  
  749. /*
  750.  * show_progress()
  751.  * Update the window title every twenty rows.
  752.  * Note that we will be called several times during
  753.  * any particular row.  There is an interlock to
  754.  * prevent the menu bar from flashing.
  755.  */
  756. void
  757. show_progress(window)
  758. WindowPtr            window;
  759. {
  760.         Str255                    title;
  761.         Ulong                        theRow, botRow;
  762.         Boolean                    doneTwenty;
  763.         static Boolean    needUpdate = FALSE;
  764.         
  765.         doneTwenty = (DOC.center.v % 20) == 0;
  766.         if (needUpdate == doneTwenty) {
  767.             if (needUpdate == FALSE)    /* Just leave 20 row?        */
  768.                 needUpdate = TRUE;            /* Get ready for update    */
  769.             else {                                        /* Just entered 20 row    */
  770.                 theRow = DOC.center.v;    /* Want row and bottom    */
  771.                 botRow = DOC.bottom;        /* as long int's                */
  772.                 NumToString((theRow * 100) / botRow, title);
  773.                 pstrcat(title, "\p% done");
  774.                 SetWTitle(window, title);
  775.                 needUpdate = FALSE;            /* Skip until next 20        */
  776.             }
  777.         }
  778. }
  779.  
  780. /*
  781.  * display_statistics shows the result of the examination
  782.  * process.
  783.  */
  784. #define SHOWLINE do {                                                                        \
  785.         TextBox(                                                                                        \
  786.             &theLine[1], theLine[0], &textRect, teJustLeft);    \
  787.         OffsetRect(&textRect, 0, lineSize);                                    \
  788.         theLine[0] = 0;                                                                            \
  789.     } while (0)
  790.  
  791. #define LINETEXT(t) do {                                                                \
  792.         pstrcpy(theLine, "\p ");                                                        \
  793.         pstrcat(theLine, t);                                                                \
  794.     } while (0)
  795. #define DRAWVALUE(v) do {                                                                \
  796.         NumToString((v), work);                                                            \
  797.         if (work[0] == 0)                                                                        \
  798.             pstrcat(theLine, "\p0");                                                    \
  799.         else {                                                                                            \
  800.             pstrcat(theLine, work);                                                        \
  801.         }                                                                                                        \
  802.     } while (0)
  803. #define DRAWTIME(v) do {                                                                \
  804.         pstrcat(theLine, "\p:");                                                        \
  805.         leading_zero(theLine, (v), 2);                                            \
  806.     } while (0) 
  807. #define LINEVALUE(before, value, after) do {                        \
  808.         LINETEXT(before);                                                                        \
  809.         DRAWVALUE(value);                                                                        \
  810.         pstrcat(theLine, "\p ");                                                        \
  811.         pstrcat(theLine, after);                                                        \
  812.         SHOWLINE;                                                                                        \
  813.     } while (0)
  814. #define RATIO(v1, v2, after) do {                                                \
  815.         compute_fraction(work, (v1), (v2));                                    \
  816.         LINETEXT(work);                                                                            \
  817.         pstrcat(theLine, "\p ");                                                        \
  818.         pstrcat(theLine, after);                                                        \
  819.         SHOWLINE;                                                                                        \
  820.     } while (0);
  821.  
  822. void                    compute_fraction(Str255, Ulong, Ulong);
  823. void                    leading_zero(Str255, Ulong, int);
  824.  
  825. /*
  826.  * Display the "About" (and/or statistics) window.
  827.  * This is excessively crude.
  828.  */
  829. void
  830. display_statistics(about_window, always)
  831. WindowPtr        about_window;
  832. Boolean            always;
  833. {
  834.         WindowPtr            window;
  835.         GrafPtr                oldPort;
  836.         FontInfo            info;
  837.         int                        i;
  838.         Boolean                more;
  839.         Str255                work, theLine;
  840.         int                        lineSize, lineHeight;
  841.         Rect                    textRect;
  842.         Ulong                    now, total, temp;
  843.         
  844.         GetPort(&oldPort);
  845.         SetPort(about_window);
  846.         TextFont(geneva);
  847.         TextSize(9);
  848.         GetFontInfo(&info);
  849.         lineHeight = info.ascent + info.descent;
  850.         lineSize = lineHeight + info.leading;
  851.         textRect = thePort->portRect;
  852.         textRect.bottom = textRect.top + lineHeight;
  853.         i = 0;
  854.         window = (WindowPtr) GetWRefCon(about_window);
  855.         if (isOurWindow(window) == FALSE
  856.          || DOC.start == 0) {
  857.             do {
  858.                 GetIndString(theLine, STRS_Info, ++i);
  859.                 more = (theLine[0] != 0);
  860.                 SHOWLINE;
  861.             } while (more);
  862.         }
  863.         else {
  864.              if (DOC.state == Working) {
  865.                  GetDateTime(&now);
  866.                 DOC.elapsed = now - DOC.start;
  867.             }
  868.             if (always
  869.              || DOC.elapsed != DOC.elapsed_shown) {
  870.                 DOC.elapsed_shown = DOC.elapsed;
  871.                 do {
  872.                     GetIndString(theLine, STRS_Info, ++i);
  873.                     more = (theLine[0] != 0);
  874.                     SHOWLINE;
  875.                 } while (more);
  876.                 total = ((Ulong) width(DOC.pictPort->portRect))
  877.                             * DOC.center.v;
  878.                 LINEVALUE("\pTotal ", total, "\ppixels.");
  879.                 LINEVALUE("\pExamined ", DOC.examined, "\ppixels.");
  880.                 LINEVALUE(
  881.                     "\pDeleted ", DOC.found, "\pnoise islands.");
  882.                 LINEVALUE(
  883.                     "\pElapsed time ", DOC.elapsed, "\pseconds.");
  884.                 LINETEXT("\pElapsed time ");
  885.                 DRAWVALUE(DOC.elapsed / 3600L);
  886.                 DRAWTIME((DOC.elapsed / 60L) % 60L);
  887.                 DRAWTIME(DOC.elapsed % 60L);
  888.                 SHOWLINE;
  889.                 RATIO(total, DOC.elapsed, "\ptotal pixels/sec.");
  890.                 RATIO(
  891.                     DOC.examined, DOC.elapsed,
  892.                     "\pexamined pixels/sec."
  893.                 );
  894.                 RATIO(
  895.                     DOC.found, DOC.examined, "\pfound / examined.");
  896.             }
  897.         }
  898.         SetPort(oldPort);
  899. }
  900.  
  901. /*
  902.  * compute_fraction()
  903.  * Convert the result (a ratio) to a printable string.
  904.  */
  905. void
  906. compute_fraction(result, numer, denom)
  907. Str255            result;
  908. Ulong                numer;
  909. Ulong                denom;
  910. {
  911.         if (denom == 0)
  912.             pstrcpy(result, "\p<0>");
  913.         else {
  914.             NumToString(numer / denom, result);
  915.             pstrcat(result, "\p.");
  916.             leading_zero(
  917.                 result, ((numer * 1000L) / denom) % 1000, 3);
  918.         }
  919. }
  920.  
  921. /*
  922.  * leading_zero()
  923.  * This is a crude function to display a value with
  924.  * leading zeros.
  925.  */
  926. void
  927. leading_zero(result, value, field_width)
  928. Str255            result;
  929. Ulong                value;
  930. int                    field_width;
  931. {
  932.         Str255                work;
  933.         register int    i;
  934.  
  935.         NumToString(value, work);
  936.         for (i = work[0]; i < field_width; i++)
  937.             pstrcat(result, "\p0");
  938.         pstrcat(result, work);
  939. }
  940.